package transport;

import java.io.*;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.util.List;

/**
 *
 */
public abstract class ProtocolAgent extends BaseAgent {
    public static final int BUFFER_SIZE = 4096;

    private final InputStream input;
    private final OutputStream output;
    private ByteBuffer buffer;
    public ProtocolAgent(Protocol protocol, Socket socket) throws IOException {
        super(protocol);

        buffer = ByteBuffer.allocate(BUFFER_SIZE);
        input = socket.getInputStream();
        output = socket.getOutputStream();
    }

    protected abstract void processMessage(ByteBuffer message);
    protected abstract void processWrite() throws IOException;

    @Override
    protected final void update() throws IOException {
        if (input.available() > 0) {
            processRead();
        }

        processWrite();

        try {
            Thread.sleep(1);
        } catch (InterruptedException ignored) {
        }
    }

    private int messageSize = -1;
    private void processRead() throws IOException {
        int res;

        while (input.available() > 0) {
            // get message size
            if (messageSize == -1) {
                if (input.available() < 4) {
                    return ;
                }

                buffer.clear();
                res = input.read(buffer.array(), 0, 4);
                assert res == 4;

                messageSize = buffer.getInt(0);

                if (messageSize > buffer.remaining()) {
                    // reallocate buffer for bigger size
                    buffer = ByteBuffer.allocate((messageSize + BUFFER_SIZE - 1) & ~(BUFFER_SIZE - 1));
                }
            }

            // read message body
            int curSize = buffer.position();

            int toRead = Math.min(messageSize - curSize, input.available());
            res = input.read(buffer.array(), buffer.position(), toRead);
            assert res == toRead;

            curSize += res;
            buffer.position(curSize);

            // process message
            if (curSize == messageSize) {
                buffer.flip();
                processMessage(buffer);

                messageSize = -1;
            }
        }
    }

    protected final void sendMessage(List<ByteBuffer> buffers) throws IOException {
        int length = 0;
        for (ByteBuffer buffer : buffers) {
            length += buffer.remaining();
        }

        output.write(length);

        for (ByteBuffer buffer : buffers) {
            output.write(buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining());
        }
    }
}
